package me.dwk.dandan.function.functions;

import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import lombok.extern.slf4j.Slf4j;
import me.dwk.dandan.function.AbstractFunction;
import me.dwk.dandan.function.CompoundVariable;
import me.dwk.dandan.function.exception.InvalidVariableException;

/**
 * Change Case Function
 *
 * Support String manipulations of:
 * <ul>
 * <li>upper case</li>
 * <li>lower case</li>
 * <li>capitalize</li>
 * <li></li>
 * </ul>
 *
 * @since 4.0
 *
 */
@Slf4j
public class ChangeCase extends AbstractFunction {
	private static final List<String>	DESC				= new LinkedList<>();
	private static final String			KEY					= "__changeCase";

	private static final int			MIN_PARAMETER_COUNT	= 1;
	private static final int			MAX_PARAMETER_COUNT	= 3;

	static {
		DESC.add("String to modify");
		DESC.add("Change case mode UPPER(default),LOWER,CAPITALIZE ");
		DESC.add("Name of variable in which to store the result (optional)");
	}

	private CompoundVariable[] values;

	@Override
	public String execute() throws InvalidVariableException {
		String originalString = values[0].execute();
		String mode = null; // default
		if (values.length > 1) {
			mode = values[1].execute();
		}
		if (StringUtils.isEmpty(mode)) {
			mode = ChangeCaseMode.UPPER.getName(); // default
		}
		String targetString = changeCase(originalString, mode);
		addVariableValue(targetString, values, 2);
		return targetString;
	}

	protected String changeCase(String originalString,
								String mode) {
		String targetString = originalString;
		// mode is case insensitive, allow upper for example
		ChangeCaseMode changeCaseMode = ChangeCaseMode.typeOf(mode.toUpperCase());
		if (changeCaseMode != null) {
			switch (changeCaseMode) {
			case UPPER:
				targetString = StringUtils.upperCase(originalString);
				break;
			case LOWER:
				targetString = StringUtils.lowerCase(originalString);
				break;
			case CAPITALIZE:
				targetString = StringUtils.capitalize(originalString);
				break;
			default:
				// default not doing nothing to string
			}
		} else {
			log.error("Unknown mode {}, returning {} unchanged", mode, targetString);
		}
		return targetString;
	}

	@Override
	public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
		checkParameterCount(parameters, MIN_PARAMETER_COUNT, MAX_PARAMETER_COUNT);
		values = parameters.toArray(new CompoundVariable[parameters.size()]);
	}

	@Override
	public String getReferenceKey() {
		return KEY;
	}

	@Override
	public List<String> getArgumentDesc() {
		return DESC;
	}

	/**
	 * ChangeCase Modes
	 *
	 * Modes for different cases
	 *
	 */
	public enum ChangeCaseMode {
		UPPER("UPPER"), LOWER("LOWER"), CAPITALIZE("CAPITALIZE");
		private String mode;

		private ChangeCaseMode(String mode) {
			this.mode = mode;
		}

		public String getName() {
			return mode;
		}

		/**
		 * Get ChangeCaseMode by mode
		 *
		 * @param mode
		 *            Change new case mode
		 * @return relevant ChangeCaseMode
		 */
		public static ChangeCaseMode typeOf(String mode) {
			EnumSet<ChangeCaseMode> allOf = EnumSet.allOf(ChangeCaseMode.class);
			for (ChangeCaseMode csm : allOf) {
				if (csm.getName().equals(mode))
					return csm;
			}
			return null;
		}
	}
}
